複数の EC2 インスタンスにアタッチされた IAM ロール(インスタンスプロファイル)を一括で置換してみた

複数の EC2 インスタンスにアタッチされた IAM ロール(インスタンスプロファイル)を一括で置換してみた

EC2 インスタンスにアタッチされた IAM ロールが置き換わっていくさまを眺めていると、どこか幸せな気持ちになります。

コンバンハ、千葉(幸)です。

今回のシチュエーションは以下です。

  • [背景]
    • 環境に複数の EC2 インスタンスが稼働しており、IAM ロール(インスタンスプロファイル)が関連づけられている
  • [やりたいこと]
    • 環境(同一リージョン)すべての EC2 インスタンスに対して、共通した IAM ロール(インスタンスプロファイル)を関連づけるように一括変更したい

「すべてのインスタンスに」「一括で同じ IAM ロールを」 という条件付きだと、同じことをしたいケースは少ないかもしれません。今回わたしは、構築の途中段階で IAM ロールの名称を変更したい(作り直したい)要件が発生したために実施する機会がありました。

AWS CLI を用いて一括変更したため、使用したコマンドを記しておきます。

一部カスタマイズすれば置き換え対象インスタンスをフィルタリングすることもできますので、適宜かいつまんで流用してもらえればと思います。

インスタンスプロファイルと IAM ロール

「EC2インスタンスに IAM ロールをアタッチする」という概念をおさらいしておきます。

EC2 インスタンス上のワークロードに IAM ロールの認証情報を利用させる場合、インスタンスプロファイルという追加のリソースが必要になります。他のサービス向けの IAM ロールでは不要な、ちょっと特殊な存在です。

わたしはインスタンスプロファイルのことをアダプターのようなものだと捉えています。インスタンスに IAM ロールを直接アタッチすることはできないので、間にインスタンスプロファイルを挟んであげるというイメージです。それだけです。

Instance Profile

インスタンスプロファイルには、一つの IAM ロールを追加します。AWS マネジメントコンソールから EC2 向けの IAM ロールを作成した場合、自動で IAM ロールと同名のインスタンスプロファイルが作成され、IAM ロールが追加されます。そのため、意識する機会が少ない方もいるかもしれません。

AWS CLI などのプログラムアクセスを使用すれば、複数のインスタンスプロファイルに同一の IAM ロールを追加することもできます。

EC2 インスタンスにはインスタンスプロファイルを関連付けます。複数のインスタンスに同一のインスタンスプロファイルを関連付けることができます。

「IAM ロールが追加されたインスタンスプロファイルを EC2 インスタンスに関連付ける」ことを指して「EC2 インスタンスに IAM ロールをアタッチする」と丸めて表現することがあります。以降、このエントリにおいてもその表現を用います。

作業における留意事項

  • EC2 インスタンスにアタッチされた IAM ロールを直接置き換えることはできないので、一度デタッチしてからアタッチしなおします
  • EC2 インスタンスに IAM ロールをアタッチし直した後、その IAM ロールの認証情報を EC2 インスタンス上のワークロードが利用可能となるまでに一定期間かかる場合があります(再起動など、OS上で必要な作業はありません)
  • 一連の置き換え作業の中で、EC2 インスタンス上のワークロードがどの IAM ロールの認証情報も利用できないタイミングが発生する可能性があります

EC2 インスタンスにアタッチされた IAM ロールを一括変更するアプローチ

今回は AWS CloudShell 上で AWS CLI を用いて実施します。

大きくは以下の 3 ステップからなります。

  1. EC2 インスタンスごとにアタッチされた IAM ロールを一覧表示する
  2. EC2 インスタンスにアタッチされた IAM ロールを一括でデタッチする
  3. EC2 インスタンスに特定の IAM ロールを一括でアタッチする

1 の「一覧表示」は、2 や 3 の後のタイミングに追加で実施してもいいでしょう。マネジメントコンソールでのインスタンスの一覧表示では「アタッチされた IAM ロール」が表示できないので、地味に便利なコマンドです。

1. EC2 インスタンスごとにアタッチされた IAM ロールを一覧表示する

使用するコマンドは以下です。

Jq を用いて出力のフィルタリングをするコマンド例は以下の通り。

aws ec2 describe-instances \
  | jq -r '.Reservations[].Instances[] | [
      .InstanceId,
      (.Tags[] | select(.Key == "Name").Value) // "NoName",
      (.IamInstanceProfile.Arn // "None" | split("/")[-1])
    ] | @tsv'

実行イメージは以下です。

i-1234567890abcdef0    WebServer       WebServerProfile
i-0987654321fedcba0    DatabaseServer  DBServerProfile
i-abcdef1234567890     AppServer       None

インスタンス ID 、インスタンスの Nameタグ(ない場合はNoName)、インスタンスプロファイル名(ない場合はNone)をタブ区切りで表示します。

インスタンスプロファイルはデフォルトではarn:aws:iam::111111111111:instance-profile/インスタンスプロファイル名のように ARN 名で返ってくるため、/でスプリットして名称のみを取り出しています。(好みです。)

2. EC2 インスタンスにアタッチされた IAM ロールを一括でデタッチする

使用するコマンドは以下です。

後者の 「インスタンスからインスタンスプロファイルをDisassociateする(IAMロールをデタッチする)」コマンドでは、アソシエーションIDの指定が必要です。アソシエーションIDの確認のために、先行して Describe するコマンドを実行します。

両者を組み合わせ、一括でデタッチする処理を組んだコマンド例が以下です。

aws ec2 describe-iam-instance-profile-associations \
  | jq -r '.IamInstanceProfileAssociations[] | select(.State == "associated") | [.AssociationId, .InstanceId, .IamInstanceProfile.Arn] | @tsv' \
  | while IFS=$'\t' read -r association_id instance_id profile_arn; do
      profile_name=$(echo $profile_arn | awk -F'/' '{print $NF}')
      echo "Disassociating profile $profile_name from instance $instance_id"
      aws ec2 disassociate-iam-instance-profile --association-id "$association_id" > /dev/null 2>&1
    done

実行イメージは以下です。(適当に出力したサンプルなので、手順1のイメージと名称や ID の関連性はありません。)

Disassociating profile WebServerRole from instance i-1234567890abcdef0
Disassociating profile DatabaseRole from instance i-0987654321fedcba0
Disassociating profile AppServerRole from instance i-abcdef1234567890
Disassociating profile MonitoringRole from instance i-11223344556677889
Disassociating profile BackupRole from instance i-99887766554433221

先行の Describe のコマンドで、アソシエーションID、インスタンスID、インスタンスプロファイル ARN を取得します。ここでインスタンス ID やインスタンスプロファイル名(ARNの一部)でフィルタリングすれば、「環境のインスタンスすべてに」より狭い範囲に適用できます。

先行のコマンドで取得した結果にループ処理を行い、メッセージの出力とともにインスタンスプロファイルの Disassociate を行います。先行のコマンドで取得したインスタンスIDやインスタンスプロファイル名はメッセージの出力でのみ使用しています。

IAM ロールの一括デタッチが終わったら、再度 1の「一覧表示」を行い、すべてNoneになった結果を確認するのもよいでしょう。

3. EC2 インスタンスに特定の IAM ロールを一括でアタッチする

使用するコマンドは以下です。

後者の 「インスタンスにインスタンスプロファイルをAssociateする(IAMロールをアタッチする)」コマンドではインスタンスIDインスタンスプロファイル名を必要とします。

インスタンスプロファイル名はあらかじめ環境変数に格納しておきます。インスタンスIDは、先行のコマンドで環境内のインスタンスのIDの一覧を取得し、後続のコマンドに渡します。ここでは「環境すべて」のインスタンスIDを取得していますが、フィルタリングも可能です。

両者を組み合わせ、一括でアタッチする処理を組んだコマンド例が以下です。

PROFILE_NAME="YourProfileName"

aws ec2 describe-instances --query 'Reservations[].Instances[].InstanceId' --output text | \
xargs -n 1 | \
while read instance_id; do
    echo "Associating profile $PROFILE_NAME with instance $instance_id"
    aws ec2 associate-iam-instance-profile --instance-id "$instance_id" --iam-instance-profile Name="$PROFILE_NAME" > /dev/null 2>&1
done

実行イメージは以下です。(これも適当に出力したサンプルなので、1や2のイメージと関連性はありません。)

Attaching profile YourProfileName to instance i-1234567890abcdef0
Attaching profile YourProfileName to instance i-0987654321fedcba0
Attaching profile YourProfileName to instance i-abcdef1234567890

後半のループ処理ではメッセージの出力とともに、インスタンスへのIAMロールのアタッチの処理を行います。IAMロールがアタッチされている状態でさらにアタッチを試みるとエラーが発生します。今回の一連の流れの想定では2の段階でIAMロールがデタッチされているため問題ありませんが、コマンドをカスタマイズして利用していた場合には、対象のインスタンスのアタッチ状況にご注意ください。

ここでも一括アタッチが完了したら、再度 1の「一覧表示」を行い意図した通りのアタッチ状況になっているか確認するとよいでしょう。

終わりに

環境に存在するすべての EC2 インスタンスにアタッチされた IAM ロールを、一括で共通のものに置き換えるコマンドを試してみました。

少しニッチなケースかもしれませんが、部分部分で流用できる箇所があるかと思いますので、参考になれば幸いです。

今回の「2.一括デタッチ」「3.一括アタッチ」は1台ずつループを回す処理なので、対象の台数が多い場合には時間がかかってしまいます。数十台程度の規模であれば問題にならないと思いますが、さらに大規模な場合には別のアプローチをご検討ください。

以上、チバユキ (@batchicchi)がお送りしました。

参考

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.